blob: c6a56c65bf644c1265dc6b41c46c67a6700fcbc9 [file] [log] [blame]
David Pursehouse8898e2f2012-11-14 07:51:03 +09001#!/usr/bin/env python
Mike Frysingerf6013762019-06-13 02:30:51 -04002# -*- coding:utf-8 -*-
Mike Frysingerf241f8c2020-02-20 17:08:43 -05003#
4# Copyright (C) 2008 The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017
Mike Frysinger87fb5a12019-06-13 01:54:46 -040018"""Repo launcher.
19
20This is a standalone tool that people may copy to anywhere in their system.
21It is used to get an initial repo client checkout, and after that it runs the
22copy of repo in the checkout.
23"""
24
Mike Frysingerc92ce5c2019-06-13 01:14:23 -040025from __future__ import print_function
26
Mike Frysinger84094102020-02-11 02:10:28 -050027import datetime
Mike Frysinger3ba716f2019-06-13 01:48:12 -040028import os
29import platform
Mike Frysinger949bc342020-02-18 21:37:00 -050030import shlex
Mike Frysinger3ba716f2019-06-13 01:48:12 -040031import subprocess
32import sys
33
34
Mike Frysinger6fb0cb52020-02-12 09:39:23 -050035# Keep basic logic in sync with repo_trace.py.
36class Trace(object):
37 """Trace helper logic."""
38
39 REPO_TRACE = 'REPO_TRACE'
40
41 def __init__(self):
42 self.set(os.environ.get(self.REPO_TRACE) == '1')
43
44 def set(self, value):
45 self.enabled = bool(value)
46
47 def print(self, *args, **kwargs):
48 if self.enabled:
49 print(*args, **kwargs)
50
51
52trace = Trace()
53
54
Mike Frysinger3ba716f2019-06-13 01:48:12 -040055def exec_command(cmd):
56 """Execute |cmd| or return None on failure."""
Mike Frysinger6fb0cb52020-02-12 09:39:23 -050057 trace.print(':', ' '.join(cmd))
Mike Frysinger3ba716f2019-06-13 01:48:12 -040058 try:
59 if platform.system() == 'Windows':
60 ret = subprocess.call(cmd)
61 sys.exit(ret)
62 else:
63 os.execvp(cmd[0], cmd)
Mike Frysinger72b6dc82020-02-12 17:04:32 -050064 except Exception:
Mike Frysinger3ba716f2019-06-13 01:48:12 -040065 pass
66
67
68def check_python_version():
69 """Make sure the active Python version is recent enough."""
70 def reexec(prog):
71 exec_command([prog] + sys.argv)
72
73 MIN_PYTHON_VERSION = (3, 6)
74
75 ver = sys.version_info
76 major = ver.major
77 minor = ver.minor
78
79 # Abort on very old Python 2 versions.
80 if (major, minor) < (2, 7):
81 print('repo: error: Your Python version is too old. '
82 'Please use Python {}.{} or newer instead.'.format(
83 *MIN_PYTHON_VERSION), file=sys.stderr)
84 sys.exit(1)
85
86 # Try to re-exec the version specific Python 3 if needed.
87 if (major, minor) < MIN_PYTHON_VERSION:
88 # Python makes releases ~once a year, so try our min version +10 to help
89 # bridge the gap. This is the fallback anyways so perf isn't critical.
90 min_major, min_minor = MIN_PYTHON_VERSION
91 for inc in range(0, 10):
92 reexec('python{}.{}'.format(min_major, min_minor + inc))
93
94 # Try the generic Python 3 wrapper, but only if it's new enough. We don't
95 # want to go from (still supported) Python 2.7 to (unsupported) Python 3.5.
96 try:
97 proc = subprocess.Popen(
98 ['python3', '-c', 'import sys; '
99 'print(sys.version_info.major, sys.version_info.minor)'],
100 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
101 (output, _) = proc.communicate()
102 python3_ver = tuple(int(x) for x in output.decode('utf-8').split())
103 except (OSError, subprocess.CalledProcessError):
104 python3_ver = None
105
106 # The python3 version looks like it's new enough, so give it a try.
107 if python3_ver and python3_ver >= MIN_PYTHON_VERSION:
108 reexec('python3')
109
110 # We're still here, so diagnose things for the user.
111 if major < 3:
112 print('repo: warning: Python 2 is no longer supported; '
113 'Please upgrade to Python {}.{}+.'.format(*MIN_PYTHON_VERSION),
114 file=sys.stderr)
115 else:
116 print('repo: error: Python 3 version is too old; '
117 'Please use Python {}.{} or newer.'.format(*MIN_PYTHON_VERSION),
118 file=sys.stderr)
119 sys.exit(1)
120
121
122if __name__ == '__main__':
Mike Frysinger19ec7972020-02-16 12:02:01 -0500123 check_python_version()
Mike Frysinger3ba716f2019-06-13 01:48:12 -0400124
125
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700126# repo default configuration
127#
Mark E. Hamilton55536282016-02-03 15:49:43 -0700128REPO_URL = os.environ.get('REPO_URL', None)
129if not REPO_URL:
130 REPO_URL = 'https://gerrit.googlesource.com/git-repo'
Mike Frysinger563f1a62020-02-05 23:52:07 -0500131REPO_REV = os.environ.get('REPO_REV')
132if not REPO_REV:
133 REPO_REV = 'stable'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700134
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700135# increment this whenever we make important changes to this script
Mike Frysinger19ec7972020-02-16 12:02:01 -0500136VERSION = (2, 4)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700137
138# increment this if the MAINTAINER_KEYS block is modified
Mike Frysinger9cc1d702020-02-13 18:28:03 -0500139KEYRING_VERSION = (2, 3)
Mike Frysingere4433652016-09-13 18:06:07 -0400140
141# Each individual key entry is created by using:
142# gpg --armor --export keyid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700143MAINTAINER_KEYS = """
144
145 Repo Maintainer <repo@android.kernel.org>
146-----BEGIN PGP PUBLIC KEY BLOCK-----
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700147
148mQGiBEj3ugERBACrLJh/ZPyVSKeClMuznFIrsQ+hpNnmJGw1a9GXKYKk8qHPhAZf
149WKtrBqAVMNRLhL85oSlekRz98u41H5si5zcuv+IXJDF5MJYcB8f22wAy15lUqPWi
150VCkk1l8qqLiuW0fo+ZkPY5qOgrvc0HW1SmdH649uNwqCbcKb6CxaTxzhOwCgj3AP
151xI1WfzLqdJjsm1Nq98L0cLcD/iNsILCuw44PRds3J75YP0pze7YF/6WFMB6QSFGu
152aUX1FsTTztKNXGms8i5b2l1B8JaLRWq/jOnZzyl1zrUJhkc0JgyZW5oNLGyWGhKD
153Fxp5YpHuIuMImopWEMFIRQNrvlg+YVK8t3FpdI1RY0LYqha8pPzANhEYgSfoVzOb
154fbfbA/4ioOrxy8ifSoga7ITyZMA+XbW8bx33WXutO9N7SPKS/AK2JpasSEVLZcON
155ae5hvAEGVXKxVPDjJBmIc2cOe7kOKSi3OxLzBqrjS2rnjiP4o0ekhZIe4+ocwVOg
156e0PLlH5avCqihGRhpoqDRsmpzSHzJIxtoeb+GgGEX8KkUsVAhbQpUmVwbyBNYWlu
157dGFpbmVyIDxyZXBvQGFuZHJvaWQua2VybmVsLm9yZz6IYAQTEQIAIAUCSPe6AQIb
158AwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEBZTDV6SD1xl1GEAn0x/OKQpy7qI
1596G73NJviU0IUMtftAKCFMUhGb/0bZvQ8Rm3QCUpWHyEIu7kEDQRI97ogEBAA2wI6
1605fs9y/rMwD6dkD/vK9v4C9mOn1IL5JCPYMJBVSci+9ED4ChzYvfq7wOcj9qIvaE0
161GwCt2ar7Q56me5J+byhSb32Rqsw/r3Vo5cZMH80N4cjesGuSXOGyEWTe4HYoxnHv
162gF4EKI2LK7xfTUcxMtlyn52sUpkfKsCpUhFvdmbAiJE+jCkQZr1Z8u2KphV79Ou+
163P1N5IXY/XWOlq48Qf4MWCYlJFrB07xjUjLKMPDNDnm58L5byDrP/eHysKexpbakL
164xCmYyfT6DV1SWLblpd2hie0sL3YejdtuBMYMS2rI7Yxb8kGuqkz+9l1qhwJtei94
1655MaretDy/d/JH/pRYkRf7L+ke7dpzrP+aJmcz9P1e6gq4NJsWejaALVASBiioqNf
166QmtqSVzF1wkR5avZkFHuYvj6V/t1RrOZTXxkSk18KFMJRBZrdHFCWbc5qrVxUB6e
167N5pja0NFIUCigLBV1c6I2DwiuboMNh18VtJJh+nwWeez/RueN4ig59gRTtkcc0PR
16835tX2DR8+xCCFVW/NcJ4PSePYzCuuLvp1vEDHnj41R52Fz51hgddT4rBsp0nL+5I
169socSOIIezw8T9vVzMY4ArCKFAVu2IVyBcahTfBS8q5EM63mONU6UVJEozfGljiMw
170xuQ7JwKcw0AUEKTKG7aBgBaTAgT8TOevpvlw91cAAwUP/jRkyVi/0WAb0qlEaq/S
171ouWxX1faR+vU3b+Y2/DGjtXQMzG0qpetaTHC/AxxHpgt/dCkWI6ljYDnxgPLwG0a
172Oasm94BjZc6vZwf1opFZUKsjOAAxRxNZyjUJKe4UZVuMTk6zo27Nt3LMnc0FO47v
173FcOjRyquvgNOS818irVHUf12waDx8gszKxQTTtFxU5/ePB2jZmhP6oXSe4K/LG5T
174+WBRPDrHiGPhCzJRzm9BP0lTnGCAj3o9W90STZa65RK7IaYpC8TB35JTBEbrrNCp
175w6lzd74LnNEp5eMlKDnXzUAgAH0yzCQeMl7t33QCdYx2hRs2wtTQSjGfAiNmj/WW
176Vl5Jn+2jCDnRLenKHwVRFsBX2e0BiRWt/i9Y8fjorLCXVj4z+7yW6DawdLkJorEo
177p3v5ILwfC7hVx4jHSnOgZ65L9s8EQdVr1ckN9243yta7rNgwfcqb60ILMFF1BRk/
1780V7wCL+68UwwiQDvyMOQuqkysKLSDCLb7BFcyA7j6KG+5hpsREstFX2wK1yKeraz
1795xGrFy8tfAaeBMIQ17gvFSp/suc9DYO0ICK2BISzq+F+ZiAKsjMYOBNdH/h0zobQ
180HTHs37+/QLMomGEGKZMWi0dShU2J5mNRQu3Hhxl3hHDVbt5CeJBb26aQcQrFz69W
181zE3GNvmJosh6leayjtI9P2A6iEkEGBECAAkFAkj3uiACGwwACgkQFlMNXpIPXGWp
Mike Frysinger9cc1d702020-02-13 18:28:03 -0500182TACbBS+Up3RpfYVfd63c1cDdlru13pQAn3NQy/SN858MkxN+zym86UBgOad2uQIN
183BF5FqOoBEAC8aRtWEtXzeuoQhdFrLTqYs2dy6kl9y+j3DMQYAMs8je582qzUigIO
184ZZxq7T/3WQgghsdw9yPvdzlw9tKdet2TJkR1mtBfSjZQrkKwR0pQP4AD7t/90Whu
185R8Wlu8ysapE2hLxMH5Y2znRQX2LkUYmk0K2ik9AgZEh3AFEg3YLl2pGnSjeSp3ch
186cLX2n/rVZf5LXluZGRG+iov1Ka+8m+UqzohMA1DYNECJW6KPgXsNX++i8/iwZVic
187PWzhRJSQC+QiAZNsKT6HNNKs97YCUVzhjBLnRSxRBPkr0hS/VMWY2V4pbASljWyd
188GYmlDcxheLne0yjes0bJAdvig5rB42FOV0FCM4bDYOVwKfZ7SpzGCYXxtlwe0XNG
189tLW9WA6tICVqNZ/JNiRTBLrsGSkyrEhDPKnIHlHRI5Zux6IHwMVB0lQKHjSop+t6
190oyubqWcPCGGYdz2QGQHNz7huC/Zn0wS4hsoiSwPv6HCq3jNyUkOJ7wZ3ouv60p2I
191kPurgviVaRaPSKTYdKfkcJOtFeqOh1na5IHkXsD9rNctB7tSgfsm0G6qJIVe3ZmJ
1927QAyHBfuLrAWCq5xS8EHDlvxPdAD8EEsa9T32YxcHKIkxr1eSwrUrKb8cPhWq1pp
193Jiylw6G1fZ02VKixqmPC4oFMyg1PO8L2tcQTrnVmZvfFGiaekHKdhQARAQABiQKW
194BBgRAgAgFiEEi7mteT6OYVOvD5pEFlMNXpIPXGUFAl5FqOoCGwICQAkQFlMNXpIP
195XGXBdCAEGQEKAB0WIQSjShO+jna/9GoMAi2i51qCSquWJAUCXkWo6gAKCRCi51qC
196SquWJLzgD/0YEZYS7yKxhP+kk94TcTYMBMSZpU5KFClB77yu4SI1LeXq4ocBT4sp
197EPaOsQiIx//j59J67b7CBe4UeRA6D2n0pw+bCKuc731DFi5X9C1zq3a7E67SQ2yd
198FbYE2fnpVnMqb62g4sTh7JmdxEtXCWBUWL0OEoWouBW1PkFDHx2kYLC7YpZt3+4t
199VtNhSfV8NS6PF8ep3JXHVd2wsC3DQtggeId5GM44o8N0SkwQHNjK8ZD+VZ74ZnhZ
200HeyHskomiOC61LrZWQvxD6VqtfnBQ5GvONO8QuhkiFwMMOnpPVj2k7ngSkd5o27K
2016c53ZESOlR4bAfl0i3RZYC9B5KerGkBE3dTgTzmGjOaahl2eLz4LDPdTwMtS+sAU
2021hPPvZTQeYDdV62bOWUyteMoJu354GgZPQ9eItWYixpNCyOGNcJXl6xk3/OuoP6f
203MciFV8aMxs/7mUR8q1Ei3X9MKu+bbODYj2rC1tMkLj1OaAJkfvRuYrKsQpoUsn4q
204VT9+aciNpU/I7M30watlWo7RfUFI3zaGdMDcMFju1cWt2Un8E3gtscGufzbz1Z5Z
205Gak+tCOWUyuYNWX3noit7Dk6+3JGHGaQettldNu2PLM9SbIXd2EaqK/eEv9BS3dd
206ItkZwzyZXSaQ9UqAceY1AHskJJ5KVXIRLuhP5jBWWo3fnRMyMYt2nwNBAJ9B9TA8
207VlBniwIl5EzCvOFOTGrtewCdHOvr3N3ieypGz1BzyCN9tJMO3G24MwReRal9Fgkr
208BgEEAdpHDwEBB0BhPE/je6OuKgWzJ1mnrUmHhn4IMOHp+58+T5kHU3Oy6YjXBBgR
209AgAgFiEEi7mteT6OYVOvD5pEFlMNXpIPXGUFAl5FqX0CGwIAgQkQFlMNXpIPXGV2
210IAQZFggAHRYhBOH5BA16P22vrIl809O5XaJD5Io5BQJeRal9AAoJENO5XaJD5Io5
211MEkA/3uLmiwANOcgE0zB9zga0T/KkYhYOWFx7zRyDhrTf9spAPwIfSBOAGtwxjLO
212DCce5OaQJl/YuGHvXq2yx5h7T8pdAZ+PAJ4qfIk2LLSidsplTDXOKhOQAuOqUQCf
213cZ7aFsJF4PtcDrfdejyAxbtsSHI=
214=82Tj
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700215-----END PGP PUBLIC KEY BLOCK-----
216"""
217
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700218GIT = 'git' # our git command
Mike Frysinger82caef62020-02-11 18:51:08 -0500219# NB: The version of git that the repo launcher requires may be much older than
220# the version of git that the main repo source tree requires. Keeping this at
221# an older version also makes it easier for users to upgrade/rollback as needed.
222#
223# git-1.7 is in (EOL) Ubuntu Precise.
224MIN_GIT_VERSION = (1, 7, 2) # minimum supported git version
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700225repodir = '.repo' # name of repo's private directory
226S_repo = 'repo' # special repo repository
227S_manifests = 'manifests' # special manifest repository
228REPO_MAIN = S_repo + '/main.py' # main script
Simran Basi8ce50412015-08-28 14:25:44 -0700229GITC_CONFIG_FILE = '/gitc/.config'
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700230GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700231
232
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400233import collections
David Jamesbf79c662013-12-26 14:20:13 -0800234import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700235import optparse
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700236import re
Mitchel Humpheryseb5acc92014-03-12 10:48:15 -0700237import shutil
Sarah Owens60798a32012-10-25 17:53:09 -0700238import stat
David Pursehouse59bbb582013-05-17 10:49:33 +0900239
240if sys.version_info[0] == 3:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700241 import urllib.request
242 import urllib.error
243else:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700244 import imp
David Pursehouse59bbb582013-05-17 10:49:33 +0900245 import urllib2
Sarah Owens1f7627f2012-10-31 09:21:55 -0700246 urllib = imp.new_module('urllib')
247 urllib.request = urllib2
248 urllib.error = urllib2
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700249
Conley Owens5e0ee142013-09-26 15:50:49 -0700250
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251home_dot_repo = os.path.expanduser('~/.repoconfig')
252gpg_dir = os.path.join(home_dot_repo, 'gnupg')
253
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900254
Mike Frysingerd8fda902020-02-14 00:24:38 -0500255def GetParser(gitc_init=False):
256 """Setup the CLI parser."""
257 if gitc_init:
258 usage = 'repo gitc-init -u url -c client [options]'
259 else:
260 usage = 'repo init -u url [options]'
261
262 parser = optparse.OptionParser(usage=usage)
263
Mike Frysingerf700ac72020-02-06 00:04:21 -0500264 # Logging.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500265 group = parser.add_option_group('Logging options')
Mike Frysingeredd3d452020-02-21 23:55:07 -0500266 group.add_option('-v', '--verbose',
267 dest='output_mode', action='store_true',
268 help='show all output')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500269 group.add_option('-q', '--quiet',
Mike Frysingeredd3d452020-02-21 23:55:07 -0500270 dest='output_mode', action='store_false',
271 help='only show errors')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700272
Mike Frysingerf700ac72020-02-06 00:04:21 -0500273 # Manifest.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500274 group = parser.add_option_group('Manifest options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500275 group.add_option('-u', '--manifest-url',
276 help='manifest repository location', metavar='URL')
277 group.add_option('-b', '--manifest-branch',
278 help='manifest branch or revision', metavar='REVISION')
279 group.add_option('-m', '--manifest-name',
280 help='initial manifest file', metavar='NAME.xml')
Mike Frysingerd8fda902020-02-14 00:24:38 -0500281 cbr_opts = ['--current-branch']
282 # The gitc-init subcommand allocates -c itself, but a lot of init users
283 # want -c, so try to satisfy both as best we can.
284 if not gitc_init:
285 cbr_opts += ['-c']
286 group.add_option(*cbr_opts,
Mike Frysingerf700ac72020-02-06 00:04:21 -0500287 dest='current_branch_only', action='store_true',
288 help='fetch only current manifest branch from server')
289 group.add_option('--mirror', action='store_true',
290 help='create a replica of the remote repositories '
291 'rather than a client working directory')
292 group.add_option('--reference',
293 help='location of mirror directory', metavar='DIR')
294 group.add_option('--dissociate', action='store_true',
295 help='dissociate from reference mirrors after clone')
296 group.add_option('--depth', type='int', default=None,
297 help='create a shallow clone with given depth; '
298 'see git clone')
299 group.add_option('--partial-clone', action='store_true',
300 help='perform partial clone (https://git-scm.com/'
301 'docs/gitrepository-layout#_code_partialclone_code)')
302 group.add_option('--clone-filter', action='store', default='blob:none',
303 help='filter for use with --partial-clone '
304 '[default: %default]')
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500305 group.add_option('--worktree', action='store_true',
306 help=optparse.SUPPRESS_HELP)
Mike Frysingerf700ac72020-02-06 00:04:21 -0500307 group.add_option('--archive', action='store_true',
308 help='checkout an archive instead of a git repository for '
309 'each project. See git archive.')
310 group.add_option('--submodules', action='store_true',
311 help='sync any submodules associated with the manifest repo')
312 group.add_option('-g', '--groups', default='default',
313 help='restrict manifest projects to ones with specified '
314 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]',
315 metavar='GROUP')
316 group.add_option('-p', '--platform', default='auto',
317 help='restrict manifest projects to ones with a specified '
318 'platform group [auto|all|none|linux|darwin|...]',
319 metavar='PLATFORM')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500320 group.add_option('--no-clone-bundle',
321 dest='clone_bundle', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500322 help='disable use of /clone.bundle on HTTP/HTTPS')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500323 group.add_option('--no-tags',
324 dest='tags', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500325 help="don't fetch tags in the manifest")
Doug Anderson49cd59b2011-06-13 21:42:06 -0700326
Mike Frysingerf700ac72020-02-06 00:04:21 -0500327 # Tool.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500328 group = parser.add_option_group('repo Version options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500329 group.add_option('--repo-url', metavar='URL',
330 help='repo repository location ($REPO_URL)')
331 group.add_option('--repo-branch', metavar='REVISION',
332 help='repo branch or revision ($REPO_REV)')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500333 group.add_option('--no-repo-verify',
334 dest='repo_verify', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500335 help='do not verify repo source code')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700336
Mike Frysingerf700ac72020-02-06 00:04:21 -0500337 # Other.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500338 group = parser.add_option_group('Other options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500339 group.add_option('--config-name',
340 action='store_true', default=False,
341 help='Always prompt for name/e-mail')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700342
Mike Frysingerd8fda902020-02-14 00:24:38 -0500343 # gitc-init specific settings.
344 if gitc_init:
345 group = parser.add_option_group('GITC options')
346 group.add_option('-f', '--manifest-file',
347 help='Optional manifest file to use for this GITC client.')
348 group.add_option('-c', '--gitc-client',
349 help='Name of the gitc_client instance to create or modify.')
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700350
Mike Frysingerd8fda902020-02-14 00:24:38 -0500351 return parser
Simran Basi1efc2b42015-08-05 15:04:22 -0700352
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900353
Mike Frysinger62285d22020-02-12 08:01:38 -0500354# This is a poor replacement for subprocess.run until we require Python 3.6+.
355RunResult = collections.namedtuple(
356 'RunResult', ('returncode', 'stdout', 'stderr'))
357
358
359class RunError(Exception):
360 """Error when running a command failed."""
361
362
363def run_command(cmd, **kwargs):
364 """Run |cmd| and return its output."""
365 check = kwargs.pop('check', False)
366 if kwargs.pop('capture_output', False):
367 kwargs.setdefault('stdout', subprocess.PIPE)
368 kwargs.setdefault('stderr', subprocess.PIPE)
369 cmd_input = kwargs.pop('input', None)
370
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500371 def decode(output):
372 """Decode |output| to text."""
373 if output is None:
374 return output
375 try:
376 return output.decode('utf-8')
377 except UnicodeError:
378 print('repo: warning: Invalid UTF-8 output:\ncmd: %r\n%r' % (cmd, output),
379 file=sys.stderr)
380 # TODO(vapier): Once we require Python 3, use 'backslashreplace'.
381 return output.decode('utf-8', 'replace')
382
Mike Frysinger62285d22020-02-12 08:01:38 -0500383 # Run & package the results.
384 proc = subprocess.Popen(cmd, **kwargs)
385 (stdout, stderr) = proc.communicate(input=cmd_input)
Mike Frysinger71928c12020-02-21 23:45:08 -0500386 dbg = ': ' + ' '.join(cmd)
387 if cmd_input is not None:
388 dbg += ' 0<|'
389 if stdout == subprocess.PIPE:
390 dbg += ' 1>|'
391 if stderr == subprocess.PIPE:
392 dbg += ' 2>|'
393 elif stderr == subprocess.STDOUT:
394 dbg += ' 2>&1'
395 trace.print(dbg)
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500396 ret = RunResult(proc.returncode, decode(stdout), decode(stderr))
Mike Frysinger62285d22020-02-12 08:01:38 -0500397
398 # If things failed, print useful debugging output.
399 if check and ret.returncode:
400 print('repo: error: "%s" failed with exit status %s' %
401 (cmd[0], ret.returncode), file=sys.stderr)
402 print(' cwd: %s\n cmd: %r' %
403 (kwargs.get('cwd', os.getcwd()), cmd), file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900404
Mike Frysinger62285d22020-02-12 08:01:38 -0500405 def _print_output(name, output):
406 if output:
407 print(' %s:\n >> %s' % (name, '\n >> '.join(output.splitlines())),
408 file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900409
Mike Frysinger62285d22020-02-12 08:01:38 -0500410 _print_output('stdout', ret.stdout)
411 _print_output('stderr', ret.stderr)
412 raise RunError(ret)
413
414 return ret
415
416
Simran Basi8ce50412015-08-28 14:25:44 -0700417_gitc_manifest_dir = None
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700418
419
Simran Basi8ce50412015-08-28 14:25:44 -0700420def get_gitc_manifest_dir():
421 global _gitc_manifest_dir
422 if _gitc_manifest_dir is None:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700423 _gitc_manifest_dir = ''
Simran Basi8ce50412015-08-28 14:25:44 -0700424 try:
425 with open(GITC_CONFIG_FILE, 'r') as gitc_config:
426 for line in gitc_config:
427 match = re.match('gitc_dir=(?P<gitc_manifest_dir>.*)', line)
428 if match:
429 _gitc_manifest_dir = match.group('gitc_manifest_dir')
430 except IOError:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700431 pass
Simran Basi8ce50412015-08-28 14:25:44 -0700432 return _gitc_manifest_dir
433
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700434
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700435def gitc_parse_clientdir(gitc_fs_path):
436 """Parse a path in the GITC FS and return its client name.
437
438 @param gitc_fs_path: A subdirectory path within the GITC_FS_ROOT_DIR.
439
440 @returns: The GITC client name
441 """
442 if gitc_fs_path == GITC_FS_ROOT_DIR:
443 return None
444 if not gitc_fs_path.startswith(GITC_FS_ROOT_DIR):
445 manifest_dir = get_gitc_manifest_dir()
446 if manifest_dir == '':
447 return None
448 if manifest_dir[-1] != '/':
449 manifest_dir += '/'
450 if gitc_fs_path == manifest_dir:
451 return None
452 if not gitc_fs_path.startswith(manifest_dir):
453 return None
454 return gitc_fs_path.split(manifest_dir)[1].split('/')[0]
455 return gitc_fs_path.split(GITC_FS_ROOT_DIR)[1].split('/')[0]
456
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700457
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700458class CloneFailure(Exception):
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700459
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700460 """Indicate the remote clone of repo itself failed.
461 """
462
463
Simran Basi1efc2b42015-08-05 15:04:22 -0700464def _Init(args, gitc_init=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700465 """Installs repo by cloning it over the network.
466 """
Mike Frysingerd8fda902020-02-14 00:24:38 -0500467 parser = GetParser(gitc_init=gitc_init)
468 opt, args = parser.parse_args(args)
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800469 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -0500470 parser.print_usage()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700471 sys.exit(1)
Mike Frysingeredd3d452020-02-21 23:55:07 -0500472 opt.quiet = opt.output_mode is False
473 opt.verbose = opt.output_mode is True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700474
Mike Frysingere1111f52020-03-14 16:28:31 -0400475 url = opt.repo_url or REPO_URL
476 branch = opt.repo_branch or REPO_REV
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700477
478 if branch.startswith('refs/heads/'):
479 branch = branch[len('refs/heads/'):]
480 if branch.startswith('refs/'):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400481 print("fatal: invalid branch name '%s'" % branch, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700482 raise CloneFailure()
483
David Jamesbf79c662013-12-26 14:20:13 -0800484 try:
Simran Basi1efc2b42015-08-05 15:04:22 -0700485 if gitc_init:
Simran Basi8ce50412015-08-28 14:25:44 -0700486 gitc_manifest_dir = get_gitc_manifest_dir()
487 if not gitc_manifest_dir:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400488 print('fatal: GITC filesystem is not available. Exiting...',
489 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -0700490 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700491 gitc_client = opt.gitc_client
492 if not gitc_client:
493 gitc_client = gitc_parse_clientdir(os.getcwd())
494 if not gitc_client:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400495 print('fatal: GITC client (-c) is required.', file=sys.stderr)
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700496 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700497 client_dir = os.path.join(gitc_manifest_dir, gitc_client)
Simran Basi1efc2b42015-08-05 15:04:22 -0700498 if not os.path.exists(client_dir):
499 os.makedirs(client_dir)
500 os.chdir(client_dir)
501 if os.path.exists(repodir):
502 # This GITC Client has already initialized repo so continue.
503 return
504
David Jamesbf79c662013-12-26 14:20:13 -0800505 os.mkdir(repodir)
506 except OSError as e:
507 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400508 print('fatal: cannot make %s directory: %s'
509 % (repodir, e.strerror), file=sys.stderr)
David Pursehouse3794a782012-11-15 06:17:30 +0900510 # Don't raise CloneFailure; that would delete the
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700511 # name. Instead exit immediately.
512 #
513 sys.exit(1)
514
515 _CheckGitVersion()
516 try:
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500517 if not opt.repo_verify:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100518 do_verify = False
Mike Frysinger910dfe82020-02-29 02:55:19 -0500519 print('repo: warning: verification of repo code has been disabled;\n'
520 'repo will not be able to verify the integrity of itself.\n',
521 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700522 else:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100523 if NeedSetupGnuPG():
524 do_verify = SetupGnuPG(opt.quiet)
525 else:
526 do_verify = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700527
Mike Frysingerdcbfadf2020-02-22 00:04:39 -0500528 if not opt.quiet:
529 print('Downloading Repo source from', url)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700530 dst = os.path.abspath(os.path.join(repodir, S_repo))
Mike Frysingeredd3d452020-02-21 23:55:07 -0500531 _Clone(url, dst, opt.clone_bundle, opt.quiet, opt.verbose)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700532
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100533 if do_verify:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700534 rev = _Verify(dst, branch, opt.quiet)
535 else:
536 rev = 'refs/remotes/origin/%s^0' % branch
537
538 _Checkout(dst, branch, rev, opt.quiet)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200539
540 if not os.path.isfile(os.path.join(dst, 'repo')):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400541 print("warning: '%s' does not look like a git-repo repository, is "
542 "REPO_URL set correctly?" % url, file=sys.stderr)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200543
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700544 except CloneFailure:
545 if opt.quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400546 print('fatal: repo init failed; run without --quiet to see why',
547 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700548 raise
549
550
Mike Frysinger62285d22020-02-12 08:01:38 -0500551def run_git(*args, **kwargs):
552 """Run git and return execution details."""
553 kwargs.setdefault('capture_output', True)
554 kwargs.setdefault('check', True)
555 try:
556 return run_command([GIT] + list(args), **kwargs)
557 except OSError as e:
558 print(file=sys.stderr)
559 print('repo: error: "%s" is not available' % GIT, file=sys.stderr)
560 print('repo: error: %s' % e, file=sys.stderr)
561 print(file=sys.stderr)
562 print('Please make sure %s is installed and in your path.' % GIT,
563 file=sys.stderr)
564 sys.exit(1)
565 except RunError:
566 raise CloneFailure()
567
568
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400569# The git version info broken down into components for easy analysis.
570# Similar to Python's sys.version_info.
571GitVersion = collections.namedtuple(
572 'GitVersion', ('major', 'minor', 'micro', 'full'))
573
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900574
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400575def ParseGitVersion(ver_str=None):
576 if ver_str is None:
577 # Load the version ourselves.
Mike Frysinger62285d22020-02-12 08:01:38 -0500578 ver_str = run_git('--version').stdout
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400579
Conley Owensff0a3c82014-01-30 14:46:03 -0800580 if not ver_str.startswith('git version '):
581 return None
582
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400583 full_version = ver_str[len('git version '):].strip()
584 num_ver_str = full_version.split('-')[0]
Conley Owensff0a3c82014-01-30 14:46:03 -0800585 to_tuple = []
586 for num_str in num_ver_str.split('.')[:3]:
587 if num_str.isdigit():
588 to_tuple.append(int(num_str))
589 else:
590 to_tuple.append(0)
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400591 to_tuple.append(full_version)
592 return GitVersion(*to_tuple)
Conley Owensff0a3c82014-01-30 14:46:03 -0800593
594
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400595def _CheckGitVersion():
Mike Frysinger62285d22020-02-12 08:01:38 -0500596 ver_act = ParseGitVersion()
Conley Owensff0a3c82014-01-30 14:46:03 -0800597 if ver_act is None:
Mike Frysinger4c263b52019-09-11 04:04:16 -0400598 print('fatal: unable to detect git version', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700599 raise CloneFailure()
600
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700601 if ver_act < MIN_GIT_VERSION:
David Pursehouse685f0802012-11-14 08:34:39 +0900602 need = '.'.join(map(str, MIN_GIT_VERSION))
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400603 print('fatal: git %s or later required' % need, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700604 raise CloneFailure()
605
606
Mike Frysinger84094102020-02-11 02:10:28 -0500607def SetGitTrace2ParentSid(env=None):
608 """Set up GIT_TRACE2_PARENT_SID for git tracing."""
609 # We roughly follow the format git itself uses in trace2/tr2_sid.c.
610 # (1) Be unique (2) be valid filename (3) be fixed length.
611 #
612 # Since we always export this variable, we try to avoid more expensive calls.
613 # e.g. We don't attempt hostname lookups or hashing the results.
614 if env is None:
615 env = os.environ
616
617 KEY = 'GIT_TRACE2_PARENT_SID'
618
619 now = datetime.datetime.utcnow()
620 value = 'repo-%s-P%08x' % (now.strftime('%Y%m%dT%H%M%SZ'), os.getpid())
621
622 # If it's already set, then append ourselves.
623 if KEY in env:
624 value = env[KEY] + '/' + value
625
626 _setenv(KEY, value, env=env)
627
628
629def _setenv(key, value, env=None):
630 """Set |key| in the OS environment |env| to |value|."""
631 if env is None:
632 env = os.environ
633 # Environment handling across systems is messy.
634 try:
635 env[key] = value
636 except UnicodeEncodeError:
637 env[key] = value.encode()
638
639
Conley Owensc9129d92012-10-01 16:12:28 -0700640def NeedSetupGnuPG():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700641 if not os.path.isdir(home_dot_repo):
642 return True
643
644 kv = os.path.join(home_dot_repo, 'keyring-version')
645 if not os.path.exists(kv):
646 return True
647
648 kv = open(kv).read()
649 if not kv:
650 return True
651
David Pursehouse685f0802012-11-14 08:34:39 +0900652 kv = tuple(map(int, kv.split('.')))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700653 if kv < KEYRING_VERSION:
654 return True
655 return False
656
657
Conley Owensc9129d92012-10-01 16:12:28 -0700658def SetupGnuPG(quiet):
David Jamesbf79c662013-12-26 14:20:13 -0800659 try:
660 os.mkdir(home_dot_repo)
661 except OSError as e:
662 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400663 print('fatal: cannot make %s directory: %s'
664 % (home_dot_repo, e.strerror), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700665 sys.exit(1)
666
David Jamesbf79c662013-12-26 14:20:13 -0800667 try:
668 os.mkdir(gpg_dir, stat.S_IRWXU)
669 except OSError as e:
670 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400671 print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
672 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700673 sys.exit(1)
674
Mike Frysinger19a1f222020-02-14 16:28:13 -0500675 if not quiet:
676 print('repo: Updating release signing keys to keyset ver %s' %
677 ('.'.join(str(x) for x in KEYRING_VERSION),))
678 # NB: We use --homedir (and cwd below) because some environments (Windows) do
679 # not correctly handle full native paths. We avoid the issue by changing to
680 # the right dir with cwd=gpg_dir before executing gpg, and then telling gpg to
681 # use the cwd (.) as its homedir which leaves the path resolution logic to it.
682 cmd = ['gpg', '--homedir', '.', '--import']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700683 try:
Mike Frysinger19a1f222020-02-14 16:28:13 -0500684 # gpg can be pretty chatty. Always capture the output and if something goes
685 # wrong, the builtin check failure will dump stdout & stderr for debugging.
686 run_command(cmd, stdin=subprocess.PIPE, capture_output=True,
687 cwd=gpg_dir, check=True,
David Pursehousec19cc5c2020-02-14 09:18:15 +0900688 input=MAINTAINER_KEYS.encode('utf-8'))
David Pursehouse22dbfb92020-02-13 08:20:55 +0900689 except OSError:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700690 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400691 print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
692 print('warning: Installing it is strongly encouraged.', file=sys.stderr)
693 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700694 return False
695
Mike Frysinger3164d402019-11-11 05:40:22 -0500696 with open(os.path.join(home_dot_repo, 'keyring-version'), 'w') as fd:
697 fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700698 return True
699
700
Mike Frysinger62285d22020-02-12 08:01:38 -0500701def _SetConfig(cwd, name, value):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700702 """Set a git configuration option to the specified value.
703 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500704 run_git('config', name, value, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700705
706
Mike Frysinger949bc342020-02-18 21:37:00 -0500707def _GetRepoConfig(name):
708 """Read a repo configuration option."""
709 config = os.path.join(home_dot_repo, 'config')
710 if not os.path.exists(config):
711 return None
712
713 cmd = ['config', '--file', config, '--get', name]
714 ret = run_git(*cmd, check=False)
715 if ret.returncode == 0:
716 return ret.stdout
717 elif ret.returncode == 1:
718 return None
719 else:
720 print('repo: error: git %s failed:\n%s' % (' '.join(cmd), ret.stderr),
721 file=sys.stderr)
722 raise RunError()
723
724
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700725def _InitHttp():
726 handlers = []
727
Sarah Owens1f7627f2012-10-31 09:21:55 -0700728 mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700729 try:
730 import netrc
731 n = netrc.netrc()
732 for host in n.hosts:
733 p = n.hosts[host]
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700734 mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800735 mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
David Pursehouse58a8b5c2020-02-13 08:21:28 +0900736 except Exception:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700737 pass
Sarah Owens1f7627f2012-10-31 09:21:55 -0700738 handlers.append(urllib.request.HTTPBasicAuthHandler(mgr))
739 handlers.append(urllib.request.HTTPDigestAuthHandler(mgr))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700740
741 if 'http_proxy' in os.environ:
742 url = os.environ['http_proxy']
Sarah Owens1f7627f2012-10-31 09:21:55 -0700743 handlers.append(urllib.request.ProxyHandler({'http': url, 'https': url}))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700744 if 'REPO_CURL_VERBOSE' in os.environ:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700745 handlers.append(urllib.request.HTTPHandler(debuglevel=1))
746 handlers.append(urllib.request.HTTPSHandler(debuglevel=1))
747 urllib.request.install_opener(urllib.request.build_opener(*handlers))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700748
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700749
Mike Frysingeredd3d452020-02-21 23:55:07 -0500750def _Fetch(url, cwd, src, quiet, verbose):
Mike Frysinger62285d22020-02-12 08:01:38 -0500751 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -0500752 if not verbose:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700753 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -0500754 err = None
755 if not quiet and sys.stdout.isatty():
756 cmd.append('--progress')
757 elif not verbose:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700758 err = subprocess.PIPE
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700759 cmd.append(src)
760 cmd.append('+refs/heads/*:refs/remotes/origin/*')
Xin Li6e538442018-12-10 11:33:16 -0800761 cmd.append('+refs/tags/*:refs/tags/*')
Mike Frysinger4847e052020-02-22 00:07:35 -0500762 run_git(*cmd, stderr=err, capture_output=False, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700763
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700764
Mike Frysingeredd3d452020-02-21 23:55:07 -0500765def _DownloadBundle(url, cwd, quiet, verbose):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700766 if not url.endswith('/'):
767 url += '/'
768 url += 'clone.bundle'
769
Mike Frysinger62285d22020-02-12 08:01:38 -0500770 ret = run_git('config', '--get-regexp', 'url.*.insteadof', cwd=cwd,
771 check=False)
772 for line in ret.stdout.splitlines():
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700773 m = re.compile(r'^url\.(.*)\.insteadof (.*)$').match(line)
774 if m:
775 new_url = m.group(1)
776 old_url = m.group(2)
777 if url.startswith(old_url):
778 url = new_url + url[len(old_url):]
779 break
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700780
781 if not url.startswith('http:') and not url.startswith('https:'):
782 return False
783
Mike Frysinger62285d22020-02-12 08:01:38 -0500784 dest = open(os.path.join(cwd, '.git', 'clone.bundle'), 'w+b')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700785 try:
786 try:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700787 r = urllib.request.urlopen(url)
788 except urllib.error.HTTPError as e:
John Törnblomd3ddcdb2015-08-12 20:12:51 +0200789 if e.code in [401, 403, 404, 501]:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700790 return False
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400791 print('fatal: Cannot get %s' % url, file=sys.stderr)
792 print('fatal: HTTP error %s' % e.code, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700793 raise CloneFailure()
Sarah Owens1f7627f2012-10-31 09:21:55 -0700794 except urllib.error.URLError as e:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400795 print('fatal: Cannot get %s' % url, file=sys.stderr)
796 print('fatal: error %s' % e.reason, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700797 raise CloneFailure()
798 try:
Mike Frysingerdcbfadf2020-02-22 00:04:39 -0500799 if verbose:
800 print('Downloading clone bundle %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700801 while True:
802 buf = r.read(8192)
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400803 if not buf:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700804 return True
805 dest.write(buf)
806 finally:
807 r.close()
808 finally:
809 dest.close()
810
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700811
Mike Frysinger62285d22020-02-12 08:01:38 -0500812def _ImportBundle(cwd):
813 path = os.path.join(cwd, '.git', 'clone.bundle')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700814 try:
Mike Frysingeredd3d452020-02-21 23:55:07 -0500815 _Fetch(cwd, cwd, path, True, False)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700816 finally:
817 os.remove(path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700818
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700819
Mike Frysingeredd3d452020-02-21 23:55:07 -0500820def _Clone(url, cwd, clone_bundle, quiet, verbose):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700821 """Clones a git repository to a new subdirectory of repodir
822 """
Mike Frysingerdcbfadf2020-02-22 00:04:39 -0500823 if verbose:
824 print('Cloning git repository', url)
825
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700826 try:
Mike Frysinger62285d22020-02-12 08:01:38 -0500827 os.mkdir(cwd)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700828 except OSError as e:
Mike Frysinger62285d22020-02-12 08:01:38 -0500829 print('fatal: cannot make %s directory: %s' % (cwd, e.strerror),
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400830 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700831 raise CloneFailure()
832
Mike Frysinger62285d22020-02-12 08:01:38 -0500833 run_git('init', '--quiet', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700834
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700835 _InitHttp()
Mike Frysinger62285d22020-02-12 08:01:38 -0500836 _SetConfig(cwd, 'remote.origin.url', url)
837 _SetConfig(cwd,
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700838 'remote.origin.fetch',
839 '+refs/heads/*:refs/remotes/origin/*')
Mike Frysingeredd3d452020-02-21 23:55:07 -0500840 if clone_bundle and _DownloadBundle(url, cwd, quiet, verbose):
Mike Frysinger62285d22020-02-12 08:01:38 -0500841 _ImportBundle(cwd)
Mike Frysingeredd3d452020-02-21 23:55:07 -0500842 _Fetch(url, cwd, 'origin', quiet, verbose)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700843
844
845def _Verify(cwd, branch, quiet):
846 """Verify the branch has been signed by a tag.
847 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500848 try:
849 ret = run_git('describe', 'origin/%s' % branch, cwd=cwd)
850 cur = ret.stdout.strip()
851 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400852 print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr)
Mike Frysinger62285d22020-02-12 08:01:38 -0500853 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700854
855 m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
856 if m:
857 cur = m.group(1)
858 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400859 print(file=sys.stderr)
860 print("info: Ignoring branch '%s'; using tagged release '%s'"
861 % (branch, cur), file=sys.stderr)
862 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700863
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800864 env = os.environ.copy()
Mike Frysinger84094102020-02-11 02:10:28 -0500865 _setenv('GNUPGHOME', gpg_dir, env)
Mike Frysinger62285d22020-02-12 08:01:38 -0500866 run_git('tag', '-v', cur, cwd=cwd, env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700867 return '%s^0' % cur
868
869
870def _Checkout(cwd, branch, rev, quiet):
871 """Checkout an upstream branch into the repository and track it.
872 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500873 run_git('update-ref', 'refs/heads/default', rev, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700874
875 _SetConfig(cwd, 'branch.default.remote', 'origin')
876 _SetConfig(cwd, 'branch.default.merge', 'refs/heads/%s' % branch)
877
Mike Frysinger62285d22020-02-12 08:01:38 -0500878 run_git('symbolic-ref', 'HEAD', 'refs/heads/default', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700879
Mike Frysinger62285d22020-02-12 08:01:38 -0500880 cmd = ['read-tree', '--reset', '-u']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700881 if not quiet:
882 cmd.append('-v')
883 cmd.append('HEAD')
Mike Frysinger62285d22020-02-12 08:01:38 -0500884 run_git(*cmd, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700885
886
887def _FindRepo():
888 """Look for a repo installation, starting at the current directory.
889 """
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200890 curdir = os.getcwd()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700891 repo = None
892
Anthony Newnamdf14a702011-01-09 17:31:57 -0800893 olddir = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200894 while curdir != '/' \
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700895 and curdir != olddir \
896 and not repo:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200897 repo = os.path.join(curdir, repodir, REPO_MAIN)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700898 if not os.path.isfile(repo):
899 repo = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200900 olddir = curdir
901 curdir = os.path.dirname(curdir)
902 return (repo, os.path.join(curdir, repodir))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700903
904
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700905class _Options(object):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700906 help = False
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500907 version = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700908
909
Mike Frysinger949bc342020-02-18 21:37:00 -0500910def _ExpandAlias(name):
911 """Look up user registered aliases."""
912 # We don't resolve aliases for existing subcommands. This matches git.
913 if name in {'gitc-init', 'help', 'init'}:
914 return name, []
915
916 alias = _GetRepoConfig('alias.%s' % (name,))
917 if alias is None:
918 return name, []
919
920 args = alias.strip().split(' ', 1)
921 name = args[0]
922 if len(args) == 2:
923 args = shlex.split(args[1])
924 else:
925 args = []
926 return name, args
927
928
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700929def _ParseArguments(args):
930 cmd = None
931 opt = _Options()
932 arg = []
933
Sarah Owensa6053d52012-11-01 13:36:50 -0700934 for i in range(len(args)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700935 a = args[i]
936 if a == '-h' or a == '--help':
937 opt.help = True
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500938 elif a == '--version':
939 opt.version = True
Mike Frysinger6fb0cb52020-02-12 09:39:23 -0500940 elif a == '--trace':
941 trace.set(True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700942 elif not a.startswith('-'):
943 cmd = a
944 arg = args[i + 1:]
945 break
946 return cmd, opt, arg
947
948
949def _Usage():
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700950 gitc_usage = ""
951 if get_gitc_manifest_dir():
952 gitc_usage = " gitc-init Initialize a GITC Client.\n"
953
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400954 print(
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700955 """usage: repo COMMAND [ARGS]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700956
957repo is not yet installed. Use "repo init" to install it here.
958
959The most commonly used repo commands are:
960
961 init Install repo in the current working directory
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700962""" + gitc_usage +
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700963 """ help Display detailed help on a command
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700964
965For access to the full online help, install repo ("repo init").
Mike Frysinger35159ab2019-06-13 00:07:13 -0400966""")
967 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700968
969
970def _Help(args):
971 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -0500972 if args[0] in {'init', 'gitc-init'}:
973 parser = GetParser(gitc_init=args[0] == 'gitc-init')
974 parser.print_help()
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700975 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700976 else:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400977 print("error: '%s' is not a bootstrap command.\n"
978 ' For access to online help, install repo ("repo init").'
979 % args[0], file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700980 else:
981 _Usage()
982 sys.exit(1)
983
984
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500985def _Version():
986 """Show version information."""
987 print('<repo not installed>')
988 print('repo launcher version %s' % ('.'.join(str(x) for x in VERSION),))
989 print(' (from %s)' % (__file__,))
990 print('git %s' % (ParseGitVersion().full,))
991 print('Python %s' % sys.version)
Mike Frysinger5f11eac2020-02-25 15:09:01 -0500992 uname = platform.uname()
993 print('OS %s %s (%s)' % (uname.system, uname.release, uname.version))
994 print('CPU %s (%s)' %
995 (uname.machine, uname.processor if uname.processor else 'unknown'))
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500996 sys.exit(0)
997
998
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700999def _NotInstalled():
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001000 print('error: repo is not installed. Use "repo init" to install it here.',
1001 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001002 sys.exit(1)
1003
1004
1005def _NoCommands(cmd):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001006 print("""error: command '%s' requires repo to be installed first.
1007 Use "repo init" to install it here.""" % cmd, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001008 sys.exit(1)
1009
1010
1011def _RunSelf(wrapper_path):
1012 my_dir = os.path.dirname(wrapper_path)
1013 my_main = os.path.join(my_dir, 'main.py')
1014 my_git = os.path.join(my_dir, '.git')
1015
1016 if os.path.isfile(my_main) and os.path.isdir(my_git):
Shawn O. Pearcec8a300f2009-05-18 13:19:57 -07001017 for name in ['git_config.py',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001018 'project.py',
1019 'subcmds']:
1020 if not os.path.exists(os.path.join(my_dir, name)):
1021 return None, None
1022 return my_main, my_git
1023 return None, None
1024
1025
1026def _SetDefaultsTo(gitdir):
1027 global REPO_URL
1028 global REPO_REV
1029
1030 REPO_URL = gitdir
Mike Frysinger62285d22020-02-12 08:01:38 -05001031 try:
1032 ret = run_git('--git-dir=%s' % gitdir, 'symbolic-ref', 'HEAD')
1033 REPO_REV = ret.stdout.strip()
1034 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001035 print('fatal: %s has no current branch' % gitdir, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036 sys.exit(1)
1037
1038
1039def main(orig_args):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001040 cmd, opt, args = _ParseArguments(orig_args)
1041
Mike Frysinger84094102020-02-11 02:10:28 -05001042 # We run this early as we run some git commands ourselves.
1043 SetGitTrace2ParentSid()
1044
Dan Willemsen745b4ad2015-10-06 15:23:19 -07001045 repo_main, rel_repo_dir = None, None
1046 # Don't use the local repo copy, make sure to switch to the gitc client first.
1047 if cmd != 'gitc-init':
1048 repo_main, rel_repo_dir = _FindRepo()
1049
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001050 wrapper_path = os.path.abspath(__file__)
1051 my_main, my_git = _RunSelf(wrapper_path)
1052
Simran Basi8ce50412015-08-28 14:25:44 -07001053 cwd = os.getcwd()
Dan Willemsen2487cb72015-08-31 15:45:06 -07001054 if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001055 print('error: repo cannot be used in the GITC local manifest directory.'
1056 '\nIf you want to work on this GITC client please rerun this '
1057 'command from the corresponding client under /gitc/',
1058 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -07001059 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001060 if not repo_main:
Mike Frysinger949bc342020-02-18 21:37:00 -05001061 # Only expand aliases here since we'll be parsing the CLI ourselves.
1062 # If we had repo_main, alias expansion would happen in main.py.
1063 cmd, alias_args = _ExpandAlias(cmd)
1064 args = alias_args + args
1065
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001066 if opt.help:
1067 _Usage()
1068 if cmd == 'help':
1069 _Help(args)
Mike Frysinger8ddff5c2020-02-09 15:00:25 -05001070 if opt.version or cmd == 'version':
1071 _Version()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001072 if not cmd:
1073 _NotInstalled()
Simran Basi1efc2b42015-08-05 15:04:22 -07001074 if cmd == 'init' or cmd == 'gitc-init':
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075 if my_git:
1076 _SetDefaultsTo(my_git)
1077 try:
Simran Basi1efc2b42015-08-05 15:04:22 -07001078 _Init(args, gitc_init=(cmd == 'gitc-init'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001079 except CloneFailure:
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001080 path = os.path.join(repodir, S_repo)
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001081 print("fatal: cloning the git-repo repository failed, will remove "
1082 "'%s' " % path, file=sys.stderr)
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001083 shutil.rmtree(path, ignore_errors=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001084 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001085 repo_main, rel_repo_dir = _FindRepo()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001086 else:
1087 _NoCommands(cmd)
1088
1089 if my_main:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001090 repo_main = my_main
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091
David Pursehouse685f0802012-11-14 08:34:39 +09001092 ver_str = '.'.join(map(str, VERSION))
anatoly techtonik3a2a59e2013-09-21 19:29:10 +03001093 me = [sys.executable, repo_main,
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001094 '--repo-dir=%s' % rel_repo_dir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001095 '--wrapper-version=%s' % ver_str,
1096 '--wrapper-path=%s' % wrapper_path,
1097 '--']
1098 me.extend(orig_args)
Mike Frysinger3ba716f2019-06-13 01:48:12 -04001099 exec_command(me)
1100 print("fatal: unable to start %s" % repo_main, file=sys.stderr)
1101 sys.exit(148)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001102
1103
1104if __name__ == '__main__':
1105 main(sys.argv[1:])